package com.capgemini.cn.authority.core.automation;

import com.capgemini.cn.authority.core.utils.SpringProxyUtils;
import com.capgemini.cn.authority.data.entity.AssertsGroup;
import com.capgemini.cn.authority.data.entity.Authorities;
import com.capgemini.cn.authority.data.entity.AuthoritiesType;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Scope;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.ws.rs.Path;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created at 2017/9/30
 *
 * @author Andriy
 */
@Component
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class AuthorizeScanner implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * 权限注册列表
     */
    private static final List<AuthorizeEntity> AUTHORIZE_REGISTER_CONTAINER = new ArrayList<>();

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    @Setter
    private AuthorizeRegisterService authorizeRegisterService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        this.logger.info("=============================开始扫描所有被标识权限的HTTP接口函数=============================");
        ApplicationContext applicationContext = event.getApplicationContext();
        if (!ObjectUtils.isEmpty(applicationContext)) {
            Map<String, Object> beansMap;
            // 判断是否Spring MVC的ApplicationContext
            if (applicationContext.getDisplayName().contains(AuthorizeDefaultValues.SPRING_MVC_DEFAULT_NAMESPACE.value())) {
                beansMap = applicationContext.getBeansWithAnnotation(Controller.class);
                this.restBeanHandle(beansMap, RequestMapping.class);
            } else {  // 否则作为JAX-RS标准进行解析处理
                beansMap = applicationContext.getBeansWithAnnotation(Path.class);
                this.restBeanHandle(beansMap, Path.class);
            }
            if (!CollectionUtils.isEmpty(AUTHORIZE_REGISTER_CONTAINER)) this.registerAuthorize();
        } else {
            this.logger.error("无法正常获取ApplicationContext，故自动注册权限数据失败！");
        }
        this.logger.info("=============================共获取到{}个被标识权限的HTTP接口函数=============================", AUTHORIZE_REGISTER_CONTAINER.size());
    }

    /**
     * 处理相关REST接口类
     *
     * @param beansMap REST接口类集合
     */
    private void restBeanHandle(Map<String, Object> beansMap, Class clazz) {
        for (Object bean : beansMap.values()) {
            bean = SpringProxyUtils.getTarget(bean);    // 处理Spring代理对象类型
            Method[] beanAllMethod = ((Class) bean).getMethods();
            for (Method beanMethod : beanAllMethod) {
                Authorize authorize = beanMethod.getDeclaredAnnotation(Authorize.class);
                Object mappingObject = beanMethod.getDeclaredAnnotation(clazz);
                // 只处理是被Spring MVC、JAX-RS和权限注解标识过并且设置为允许自动注册的方法
                if (!ObjectUtils.isEmpty(mappingObject) && !ObjectUtils.isEmpty(authorize) && authorize.autoRegister()) {
                    AuthorizeEntity authorizeEntity = new AuthorizeEntity(authorize.type(), authorize.name(), authorize.code(), authorize.groups(), authorize.assetValue(), authorize.autoRegister());
                    // 对子路径做兼容处理
                    if (authorizeEntity.getAssetValue().indexOf('/') == 0)
                        authorizeEntity.setAssetValue(authorizeEntity.getAssetValue().substring(1));
                    // 如果当前REST接口有设置父路径则需添加进去
                    Object beanMapping = ((Class) bean).getDeclaredAnnotation(clazz);
                    if (!ObjectUtils.isEmpty(beanMapping)) {
                        String[] mappingPaths;
                        if (beanMapping instanceof RequestMapping) {
                            mappingPaths = ((RequestMapping) beanMapping).path();
                        } else if (beanMapping instanceof Path) {
                            mappingPaths = new String[]{
                                    ((Path) beanMapping).value()
                            };
                        } else {
                            // 如果无法满足以上情况则直接初始化成一个空数组
                            mappingPaths = new String[0];
                        }
                        for (String path : mappingPaths) {
                            // 解析出父路径
                            if (path.indexOf('/') == 0) path = path.substring(1);
                            if (path.lastIndexOf('/') == (path.length() - 1))
                                path = path.substring(0, path.length() - 1);
                            // 组装出完成的路径地址
                            authorizeEntity.setAssetValue("/" + path + "/" + authorizeEntity.getAssetValue());
                        }
                    }
                    try {
                        String authJsonStr = new ObjectMapper().writeValueAsString(authorizeEntity);
                        this.logger.info("Authorized \"{}\" onto {}", authJsonStr, beanMethod.toString());
                    } catch (JsonProcessingException e) {
                        this.logger.warn("打印权限注册信息日志时出错！");
                    }
                    // 添加至全局容器中待后续处理
                    AUTHORIZE_REGISTER_CONTAINER.add(authorizeEntity);
                }
            }
        }
    }

    /**
     * 将权限注册至数据库中
     */
    private void registerAuthorize() {
        // 初始化自动注册时所需的一切工作
        Object[] initResult = this.authorizeRegisterService.initialization();
        AssertsGroup assertsGroup = (AssertsGroup) initResult[0];
        AuthoritiesType authoritiesType = (AuthoritiesType) initResult[1];
        Authorities authorities = (Authorities) initResult[2];
        // 执行系统资源和权限初始化工作
        final List[] registerResult = this.authorizeRegisterService.register(assertsGroup, authorities, AUTHORIZE_REGISTER_CONTAINER);
        final List<String> assetValueList = registerResult[0];
        final List<String> authorityCodeList = registerResult[1];
        // 查出已经不在权限注册列表中的资源和权限信息
        this.authorizeRegisterService.deleteScrapAssetsData(assetValueList.toArray(new String[assetValueList.size()]));
        this.authorizeRegisterService.deleteScrapAuthoritiesData(authoritiesType, authorityCodeList.toArray(new String[authorityCodeList.size()]));
    }
}
